package aqs;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * AbstractQueuedSynchronizer 同步队列器, 用于构建锁和其他同步组建的基础框架
 * 使用一个变量表示同步状态, 队列完成线程的排队
 * 3个主要操作方法: getState() setState() compareAndSetState()
 * 锁面向使用者, 同步器面向锁的实现者
 * 
 * 同步器基于模板方法模式, 也就是说需要继承同步器并重写指定的方法
 * 然后在组件的实现中使用模板方法
 * 
 * 使用同步器来实现一个独占锁
 * 
 * @author insight
 * @since 2021/9/13
 */
public class Mutex implements Lock {
    /**
     * 自定义同步器
     */
    private static class Sync extends AbstractQueuedSynchronizer {
        /**
         * 释放处于被占用状态
         */
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        /**
         * 尝试获取锁
         */
        @Override
        protected boolean tryAcquire(int arg) {
            // 如果状态为0 且修改锁成功 就设置锁的拥有者为当前线程
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        /**
         * 尝试释放锁
         */
        @Override
        protected boolean tryRelease(int arg) {
            if (getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        
        private Condition newCondition() {
            return new ConditionObject();
        }
    }

    private final Sync sync = new Sync();
    
    public boolean isLocked() {
        return sync.isHeldExclusively();
    }

    @Override
    public void lock() {
        sync.acquire(1);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    @Override
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }

    @Override
    public void unlock() {
        sync.release(1);
    }

    @Override
    public Condition newCondition() {
        return sync.newCondition();
    }
    
    public boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }
}